using System;
using System.Text;
using NHibernate;
using System.Collections.Generic;
using Technoera.Common.Infrastructure;
using Technoera.Common.QueryModel;
using NHibernate.Criterion;

namespace Technoera.Common.Nh
{
    public class NhHelper
    {
        public static void TryCloseAndDispose(ISession session)
        {
            try
            {
                if (session.IsOpen)
                {
                    session.Close();
                }
                session.Dispose();
            }
            catch
            {
                //TODO: Log error
            }
        }

        public static NhPersistenceContext CheckPersistenceContextType(IPersistenceContext persistenceContext)
        {
            if (!(persistenceContext is NhPersistenceContext))
            {
                throw new ArgumentException("IPersistenceContext parameter bust be of type NhPersistenceContext.");
            }
            return (NhPersistenceContext)persistenceContext;
        }

        public static ICriterion GetTextCriterion(string propertyName, TextSearchType textSearchType, string searchKey)
        {
            switch (textSearchType)
            {
                case TextSearchType.IsExactly:
                    return Expression.Eq(propertyName, searchKey);
                case TextSearchType.StartsWith:
                    return Expression.Like(propertyName, searchKey, MatchMode.Start);
                case TextSearchType.Contains:
                    return Expression.Like(propertyName, string.Format("%{0}%", searchKey), MatchMode.Anywhere);
                case TextSearchType.EndsWith:
                    return Expression.Like(propertyName, searchKey, MatchMode.End);
                default:
                    throw new NotSupportedException(textSearchType.ToString());
            }
        }

        public static void AddOrders(NHibernate.ICriteria criteria, SortInfo sortInfo)
        {
            foreach (SortParameter parameter in sortInfo.Parameters)
            {
                switch (parameter.SortDirection)
                {
                    case SortDirection.Ascending:
                        criteria.AddOrder(Order.Asc(parameter.SortExpression));
                        break;
                    case SortDirection.Descending:
                        criteria.AddOrder(Order.Desc(parameter.SortExpression));
                        break;
                    default:
                        throw new NotSupportedException(parameter.SortDirection.ToString());
                }
            }
        }

        #region Read

        public static IList<T> GetList<T>(ISession session, ICriteria criteria)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                return criteria.List<T>();
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    IList<T> result = criteria.List<T>();
                    transaction.Commit();
                    return result;
                }
            }
        }

        public static IList<T> GetList<T>(ISession session, IQuery query)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                return query.List<T>();
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    IList<T> result = query.List<T>();
                    transaction.Commit();
                    return result;
                }
            }
        }

        public static T GetUniqueResult<T>(ISession session, IQuery query)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                return GetUniqueResult<T>(query);
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    T result = GetUniqueResult<T>(query);
                    transaction.Commit();
                    return result;
                }
            }
        }

        private static T GetUniqueResult<T>(IQuery query)
        {
            object result = query.UniqueResult(); //UniqueResult<T> contains a bug... :-(
            if (result == null)
            {
                return default(T);
            }
            else
            {
                return (T)result;
            }
        }

        public static T GetUniqueResult<T>(ISession session, ICriteria criteria)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                return GetUniqueResult<T>(criteria);
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    T result = GetUniqueResult<T>(criteria);
                    transaction.Commit();
                    return result;
                }
            }
        }

        private static T GetUniqueResult<T>(ICriteria criteria)
        {
            object result = criteria.UniqueResult(); //Actually UniqueResult<T> seems to contain a bug... :-(
            if (result == null)
            {
                return default(T);
            }
            else
            {
                return (T)result;
            }
        }

        #endregion

        #region Write

        public static void Insert<T>(T entity, ISession session)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                session.Save(entity);
                session.Flush();
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    try
                    {
                        session.Save(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();
                        throw;
                    } 
                }
            }
        }

        public static void Update<T>(T entity, ISession session)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                session.Update(entity);
                session.Flush();
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    try
                    {
                        session.Update(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();                            
                        throw;
                    }
                }
            }
        }

        public static void InsertOrUpdate<T>(T entity, ISession session)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                session.SaveOrUpdate(entity);
                session.Flush();
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    try
                    {
                        session.SaveOrUpdate(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();                            
                        throw;
                    }
                }
            }
        }

        public static void Delete<T>(T entity, ISession session)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                session.Delete(entity);
                session.Flush();
            }
            else
            {
                using (ITransaction transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    try
                    {
                        session.Delete(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();                            
                        throw;
                    }
                }
            }
        }

        #endregion
    }
}
